#include "bsp_codec.h"
#include "esp_codec_dev.h"
#include "esp_codec_dev_defaults.h"
#include <esp_log.h>
#include <esp_err.h>
#include <assert.h>
#include <driver/i2c.h>
#include <driver/i2s_std.h>
#include "bsp_config.h"

#define TAG "[BSP] Codec"

// 编码器句柄，通过这个指针，指向es8311的结构体
static esp_codec_dev_handle_t codec_dev = NULL;

static void bsp_i2s_init(i2s_chan_handle_t *tx_handle_p, i2s_chan_handle_t *rx_handle_p)
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(BSP_CODEC_I2S_PORT, I2S_ROLE_MASTER);
    chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, tx_handle_p, rx_handle_p));

    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(BSP_CODEC_SAMPLE_RATE),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(BSP_CODEC_BIT_PER_SAMPLE, BSP_CODEC_CHANNEL_COUNT),
        .gpio_cfg = {
            .mclk = BSP_CODEC_I2S_MCK_PIN,
            .bclk = BSP_CODEC_I2S_BCK_PIN,
            .ws = BSP_CODEC_I2S_WS_PIN,
            .dout = BSP_CODEC_I2S_DO_PIN,
            .din = BSP_CODEC_I2S_DI_PIN,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false,
            },
        },
    };
    std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_128;

    ESP_ERROR_CHECK(i2s_channel_init_std_mode(*tx_handle_p, &std_cfg));
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(*rx_handle_p, &std_cfg));
    ESP_ERROR_CHECK(i2s_channel_enable(*tx_handle_p));
    ESP_ERROR_CHECK(i2s_channel_enable(*rx_handle_p));
}

static void bsp_i2c_init(void)
{
    const i2c_config_t es_i2c_cfg = {
        .sda_io_num = BSP_CODEC_I2C_SDA_PIN,
        .scl_io_num = BSP_CODEC_I2C_SCL_PIN,
        .mode = I2C_MODE_MASTER,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 100000,
    };
    ESP_ERROR_CHECK(i2c_param_config(BSP_CODEC_I2C_PORT, &es_i2c_cfg));
    ESP_ERROR_CHECK(i2c_driver_install(BSP_CODEC_I2C_PORT, I2C_MODE_MASTER, 0, 0, 0));
}

void bsp_codec_init(void)
{
    if (codec_dev != NULL)
    {
        ESP_LOGW(TAG, "Codec has been initialized");
        return;
    }

    i2s_chan_handle_t tx_handle = NULL;
    i2s_chan_handle_t rx_handle = NULL;

    bsp_i2s_init(&tx_handle, &rx_handle);
    // 构建数据接口
    audio_codec_i2s_cfg_t i2s_cfg = {
        .port = BSP_CODEC_I2S_PORT,
        .tx_handle = tx_handle,
        .rx_handle = rx_handle,
    };
    const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);

    bsp_i2c_init();
    audio_codec_i2c_cfg_t i2c_cfg = {
        .port = BSP_CODEC_I2C_PORT,
        .addr = ES8311_CODEC_DEFAULT_ADDR,
    };
    const audio_codec_ctrl_if_t *out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);

    const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();

    es8311_codec_cfg_t es8311_cfg = {
        .codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
        .ctrl_if = out_ctrl_if,
        .gpio_if = gpio_if,
        .pa_pin = BSP_CODEC_PA_PIN,
        .use_mclk = true,
        .mclk_div = I2S_MCLK_MULTIPLE_128,
    };
    const audio_codec_if_t *out_codec_if = es8311_codec_new(&es8311_cfg);

    esp_codec_dev_cfg_t codec_cfg = {
        .dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
        .codec_if = out_codec_if,
        .data_if = data_if,
    };

    codec_dev = esp_codec_dev_new(&codec_cfg);
    assert(codec_dev);

    esp_codec_dev_sample_info_t sample_info = {
        .bits_per_sample = BSP_CODEC_BIT_PER_SAMPLE,
        .channel = BSP_CODEC_CHANNEL_COUNT,
        .sample_rate = BSP_CODEC_SAMPLE_RATE,
        .channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0),
        .mclk_multiple = I2S_MCLK_MULTIPLE_128,
    };

    ESP_ERROR_CHECK(esp_codec_dev_open(codec_dev, &sample_info));
}

void bsp_codec_read(void *buf, size_t len)
{
    int ret = esp_codec_dev_read(codec_dev, buf, len);
    switch (ret)
    {
    case ESP_CODEC_DEV_INVALID_ARG:
        ESP_LOGE(TAG, "Codec Read Error: Invalid Arguments");
        break;
    case ESP_CODEC_DEV_NOT_SUPPORT:
        ESP_LOGE(TAG, "Codec Read Error: Not Support");
        break;
    case ESP_CODEC_DEV_WRONG_STATE:
        ESP_LOGE(TAG, "Codec Read Error: Wrong State");
        break;
    default:
        break;
    }
}

void bsp_codec_write(void *buf, size_t len)
{
    ESP_ERROR_CHECK(esp_codec_dev_write(codec_dev, buf, len));
}

void bsp_codec_set_mic_gain(float gain)
{
    ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(codec_dev, gain));
}

void bsp_codec_set_volume(int vol)
{
    ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(codec_dev, vol));
}

void bsp_codec_mute(bool mute)
{
    ESP_ERROR_CHECK(esp_codec_dev_set_out_mute(codec_dev, mute));
}
